package de.upb.aquarium.api.shader;

import java.io.File;

import javax.media.opengl.GL;

import com.sun.opengl.cg.CGcontext;
import com.sun.opengl.cg.CGparameter;
import com.sun.opengl.cg.CgGL;

import de.upb.aquarium.World;

public class PhongPerPixelShader {
	private final static File vertexShaderProgramm		= new File("./shaders/WaterPerPixel.vp");
	private final static File fragmentShaderProgramm	= new File("./shaders/WaterPerPixel.fp");
	
	private CgShader vertexShader;
	private CgShader fragmentShader;

	/*
	 * Fragment program parameters: 
	 */
	private CGparameter inverseModelView;

	private CGparameter absorptionAmbient;
	private CGparameter absorptionDiffuse;	
	private CGparameter absorptionSpecular;
	private CGparameter shininess;
	
	private CGparameter lightPosition;
	private CGparameter intensityAmbient;
	private CGparameter intensityDiffuse;
	private CGparameter intensitySpecular;

	private CGparameter bumpMap;
	private CGparameter bumpMapSize;

	/*
	 * Vertex program parameters:
	 */
	private CGparameter modelViewProjection;
	
	public PhongPerPixelShader(CGcontext context) {
		CgGL.cgGLSetManageTextureParameters(context, true);
		
		vertexShader		= new CgShader(vertexShaderProgramm, context, CgGL.CG_GL_VERTEX);
		fragmentShader		= new CgShader(fragmentShaderProgramm, context, CgGL.CG_GL_FRAGMENT);
		
		// Fragment parameters:
		inverseModelView	= fragmentShader.getProgramParameter("modelViewInv");
		
		absorptionAmbient	= fragmentShader.getProgramParameter("Ka");
		absorptionDiffuse	= fragmentShader.getProgramParameter("Kd");
		absorptionSpecular	= fragmentShader.getProgramParameter("Ks");
		shininess			= fragmentShader.getProgramParameter("shininess");

		lightPosition		= fragmentShader.getProgramParameter("lightPosition");
		intensityAmbient	= fragmentShader.getProgramParameter("Ia");
		intensityDiffuse	= fragmentShader.getProgramParameter("Id");
		intensitySpecular	= fragmentShader.getProgramParameter("Is");
		
		bumpMap				= fragmentShader.getProgramParameter("bumpMap");
		bumpMapSize			= fragmentShader.getProgramParameter("bumpMapSize");
		
		// Vertex parameters:
		modelViewProjection = vertexShader.getProgramParameter("modelViewProj");
	}
	
	public void activate()
	{	
		// Set projection matrices.
		CgGL.cgGLSetStateMatrixParameter(modelViewProjection,
				CgGL.CG_GL_MODELVIEW_PROJECTION_MATRIX, CgGL.CG_GL_MATRIX_IDENTITY);
		
		CgGL.cgGLSetStateMatrixParameter(inverseModelView,
				CgGL.CG_GL_MODELVIEW_MATRIX, CgGL.CG_GL_MATRIX_INVERSE);
		
		setLights();
		
		// Activate shaders
		vertexShader.activate();
		fragmentShader.activate();
	}
	
	public void deactivate()
	{
		fragmentShader.deactivate();
		vertexShader.deactivate();
	}
	
	public void setAmbientAbsorption(float r, float g, float b)
	{
		CgGL.cgGLSetParameter3f(absorptionAmbient, r, g, b);
	}
	
	public void setDiffuseAbsorption(float r, float g, float b)
	{
		CgGL.cgGLSetParameter3f(absorptionDiffuse, r, g, b);
	}
	
	public void setSpecularAbsorption(float r, float g, float b, float shininess)
	{
		CgGL.cgGLSetParameter3f(absorptionSpecular, r, g, b);
		CgGL.cgGLSetParameter1f(this.shininess, shininess);
	}

	public void setBumpMap(int textureId, double width, double height)
	{
		CgGL.cgGLSetTextureParameter(bumpMap, textureId);
		CgGL.cgGLSetParameter2d(bumpMapSize, width, height);
	}
	
	private void setLights()
	{
		GL gl = World.getInstance().getGL();
		
		int lightIndex = 0;
		for (int light = GL.GL_LIGHT0; light < GL.GL_LIGHT0 + 8; light++)
		{
			CGparameter position = CgGL.cgGetArrayParameter(lightPosition, lightIndex);
			CGparameter ambient  = CgGL.cgGetArrayParameter(intensityAmbient, lightIndex);
			CGparameter diffuse  = CgGL.cgGetArrayParameter(intensityDiffuse, lightIndex);
			CGparameter specular = CgGL.cgGetArrayParameter(intensitySpecular, lightIndex);
			lightIndex++;
			
			float[] array = new float[4];
			
			if (gl.glIsEnabled(light))
			{
				gl.glGetLightfv(light, GL.GL_POSITION, array, 0);
				CgGL.cgGLSetParameter4fv(position, array, 0);
				
				gl.glGetLightfv(light, GL.GL_AMBIENT, array, 0);
				CgGL.cgGLSetParameter4fv(ambient, array, 0);
				gl.glGetLightfv(light, GL.GL_DIFFUSE, array, 0);
				CgGL.cgGLSetParameter4fv(diffuse, array, 0);
				gl.glGetLightfv(light, GL.GL_SPECULAR, array, 0);
				CgGL.cgGLSetParameter4fv(specular, array, 0);
			}
			else
			{
				array[0] = array[1] = array[2] = array[3] = 0.0f;
				
				CgGL.cgGLSetParameter4fv(position, array, 0);
				
				CgGL.cgGLSetParameter4fv(ambient, array, 0);
				CgGL.cgGLSetParameter4fv(diffuse, array, 0);
				CgGL.cgGLSetParameter4fv(specular, array, 0);
			}
		}
	}
}
